package bancosys.tec.persist.entities.domain;

import java.util.HashMap;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import jmine.tec.persist.annotation.Alias;
import jmine.tec.persist.annotation.Comment;
import jmine.tec.persist.annotation.Comments;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.MapKeyManyToMany;

import bancosys.tec.persist.authorization.annotation.DisplayName;
import bancosys.tec.utils.date.Date;

/**
 * @created 11.07.2007
 * @author Gustavo Almeida
 */
@Entity
@Table(name = "PAI_COM_MAPA")
@Comments({ @Comment(table = "PAI_COM_MAPA", value = "TABELA SEM COMENTARIO"),
        @Comment(table = "PAI_COM_MAPA", column = "ID", value = "CODIGO") })
@Alias("PAIMAP")
@DisplayName("Pai com Mapa (BO p/ UnitTest)")
public class PaiComMapa extends Filho {

    private Map<Long, Filho> mapaLongSemCascade = new HashMap<Long, Filho>();

    private Map<String, Filho> mapaStringComCascade = new HashMap<String, Filho>();

    private Map<String, Filho> mapaStringSemCascade = new HashMap<String, Filho>();

    private Map<Filho, Filho> mapaEntityComCascade = new HashMap<Filho, Filho>();

    private Map<Filho, Filho> mapaEntitySemCascade = new HashMap<Filho, Filho>();

    private Map<Date, Filho> mapaDateComCascade = new HashMap<Date, Filho>();

    /**
     * Mapa cuja chave é um Long, sem cascade. Caso uma propriedade do Valor seja usada como chave para o mapa, deve-se anotar o mapa com
     * <code>@MapKey(name="xxx")</code>, onde xxx é o nome pra propriedade. Nesse caso, cuidado deve ser tomado ao se alterar a propriedade
     * no valor, pois ela será automaticamente replicada no mapa (alterando a chave) na próxima vez que ele for carredado do banco. Não faz
     * sentido usar o mapa de id com cascade, uma vez que o id não existe na hora da inserção.
     * 
     * @return the mapaLongSemCascade
     */
    @ManyToMany
    @org.hibernate.annotations.MapKey(columns = @Column(name = "CHAVE", nullable = false))
    @JoinTable(name = "PAI_MAPA_SEM_CASCADE_LONG", joinColumns = @JoinColumn(name = "PAI_ID", nullable = false), inverseJoinColumns = @JoinColumn(name = "FILHO_ID", nullable = false))
    @Comments({ @Comment(table = "PAI_MAPA_SEM_CASCADE_LONG", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_SEM_CASCADE_LONG", column = "PAI_ID", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_SEM_CASCADE_LONG", column = "CHAVE", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_SEM_CASCADE_LONG", column = "FILHO_ID", value = "SEM COMENTARIO.") })
    @Alias("PMSCL")
    @DisplayName("Mapa sem cascade Long")
    public Map<Long, Filho> getMapaLongSemCascade() {
        return this.mapaLongSemCascade;
    }

    /**
     * @param mapaLongSemCascade the mapaLongSemCascade to set
     */
    public void setMapaLongSemCascade(Map<Long, Filho> mapaLongSemCascade) {
        this.mapaLongSemCascade = mapaLongSemCascade;
    }

    /**
     * Mapa cuja chave e um String, com cascade. Caso uma propriedade do Valor seja usada como chave para o mapa, deve-se anotar o mapa com
     * <code>@MapKey(name="xxx")</code>, onde xxx é o nome pra propriedade. Nesse caso, cuidado deve ser tomado ao se alterar a propriedade
     * no valor, pois ela será automaticamente replicada no mapa (alterando a chave) na próxima vez que ele for carredado do banco.
     * 
     * @return the mapaStringComCascade
     */
    @OneToMany
    @Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
    @JoinTable(name = "PAI_MAPA_COM_CASCADE_STRING", joinColumns = @JoinColumn(name = "PAI_ID", nullable = false), inverseJoinColumns = @JoinColumn(name = "FILHO_ID", nullable = false))
    @Comments({ @Comment(table = "PAI_MAPA_COM_CASCADE_STRING", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_COM_CASCADE_STRING", column = "PAI_ID", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_COM_CASCADE_STRING", column = "FILHO_ID", value = "SEM COMENTARIO.") })
    @Alias("PMCCS")
    @DisplayName("Mapa com cascade String")
    @MapKey(name = "name")
    public Map<String, Filho> getMapaStringComCascade() {
        return this.mapaStringComCascade;
    }

    /**
     * @param mapaComCascade the mapaStringComCascade to set
     */
    public void setMapaStringComCascade(Map<String, Filho> mapaStringComCascade) {
        this.mapaStringComCascade = mapaStringComCascade;
    }

    /**
     * Mapa cuja chave e um String, sem cascade. Caso uma propriedade do Valor seja usada como chave para o mapa, deve-se anotar o mapa com
     * <code>@MapKey(name="xxx")</code>, onde xxx é o nome pra propriedade. Nesse caso, cuidado deve ser tomado ao se alterar a propriedade
     * no valor, pois ela será automaticamente replicada no mapa (alterando a chave) na próxima vez que ele for carredado do banco.
     * 
     * @return the mapaStringSemCascade
     */
    @ManyToMany
    @org.hibernate.annotations.MapKey(columns = @Column(name = "CHAVE", nullable = false))
    @JoinTable(name = "PAI_MAPA_SEM_CASCADE_STRING", joinColumns = @JoinColumn(name = "PAI_ID", nullable = false), inverseJoinColumns = @JoinColumn(name = "FILHO_ID", nullable = false))
    @Comments({ @Comment(table = "PAI_MAPA_SEM_CASCADE_STRING", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_SEM_CASCADE_STRING", column = "PAI_ID", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_SEM_CASCADE_STRING", column = "CHAVE", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_SEM_CASCADE_STRING", column = "FILHO_ID", value = "SEM COMENTARIO.") })
    @Alias("PMSCS")
    @DisplayName("Mapa sem cascade String")
    public Map<String, Filho> getMapaStringSemCascade() {
        return this.mapaStringSemCascade;
    }

    /**
     * @param mapaSemCascade the mapaStringSemCascade to set
     */
    public void setMapaStringSemCascade(Map<String, Filho> mapaStringSemCascade) {
        this.mapaStringSemCascade = mapaStringSemCascade;
    }

    /**
     * Mapa cuja chave e uma Entidade qualquer, com cascade. A Entidade já deve estar persistida. O Hibernate utiliza o campo id da entidade
     * para referenciar o valor internamente no banco. Portanto é essencial que o HashCode da Entidade seja baseada no id, e que ele exista
     * a priori.
     * 
     * @return the mapaEntityComCascade
     */
    @ManyToMany
    @MapKeyManyToMany(joinColumns = @JoinColumn(name = "CHAVE_ID", nullable = false))
    @JoinTable(name = "PAI_MAPA_COM_CASCADE_ENTITY", joinColumns = @JoinColumn(name = "PAI_ID", nullable = false), inverseJoinColumns = @JoinColumn(name = "FILHO_ID", nullable = false))
    @Comments({ @Comment(table = "PAI_MAPA_COM_CASCADE_ENTITY", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_COM_CASCADE_ENTITY", column = "PAI_ID", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_COM_CASCADE_ENTITY", column = "CHAVE_ID", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_COM_CASCADE_ENTITY", column = "FILHO_ID", value = "SEM COMENTARIO.") })
    @Alias("PMCCE")
    @Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
    @DisplayName("Mapa com cascade Entidade")
    @ForeignKey(name = "FK_PAIMAP_PMCCE_1", inverseName = "FK_FILHO_PMCCE_1")
    public Map<Filho, Filho> getMapaEntityComCascade() {
        return this.mapaEntityComCascade;
    }

    /**
     * @param mapaEntityComCascade the mapaEntityComCascade to set
     */
    public void setMapaEntityComCascade(Map<Filho, Filho> mapaEntityComCascade) {
        this.mapaEntityComCascade = mapaEntityComCascade;
    }

    /**
     * Mapa cuja chave e uma Entidade qualquer, sem cascade. A Entidade já deve estar persistida. O Hibernate utiliza o campo id da entidade
     * para referenciar o valor internamente no banco. Portanto é essencial que o HashCode da Entidade seja baseada no id, e que ele exista
     * a priori.
     * 
     * @return the mapaEntitySemCascade
     */
    @ManyToMany
    @MapKeyManyToMany(joinColumns = @JoinColumn(name = "CHAVE_ID", nullable = false))
    @JoinTable(name = "PAI_MAPA_SEM_CASCADE_ENTITY", joinColumns = @JoinColumn(name = "PAI_ID", nullable = false), inverseJoinColumns = @JoinColumn(name = "FILHO_ID", nullable = false))
    @Comments({ @Comment(table = "PAI_MAPA_SEM_CASCADE_ENTITY", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_SEM_CASCADE_ENTITY", column = "CHAVE_ID", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_SEM_CASCADE_ENTITY", column = "PAI_ID", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_SEM_CASCADE_ENTITY", column = "FILHO_ID", value = "SEM COMENTARIO.") })
    @Alias("PMSCE")
    @DisplayName("Mapa sem cascade Entidade")
    @ForeignKey(name = "FK_PAIMAP_PMSCE_1", inverseName = "FK_FILHO_PMSCE_1")
    public Map<Filho, Filho> getMapaEntitySemCascade() {
        return this.mapaEntitySemCascade;
    }

    /**
     * @param mapaEntitySemCascade the mapaEntitySemCascade to set
     */
    public void setMapaEntitySemCascade(Map<Filho, Filho> mapaEntitySemCascade) {
        this.mapaEntitySemCascade = mapaEntitySemCascade;
    }

    @Override
    public String toString() {
        return "Pai com mapa (" + this.getId() + "," + this.getName() + ")";
    }

    /**
     * Mapa cuja chave é uma Data qualquer, com cascade. A data não é uma propriedade do valor do mapa. Caso seja, deve-se utilizar o
     * {@link javax.persistence.MapKey} com o campo name apontando para a propriedade.
     * 
     * @return the mapaDateComCascade
     */
    @ManyToMany
    @org.hibernate.annotations.MapKey(columns = @Column(name = "DATA", nullable = false), targetElement = java.util.Date.class)
    @JoinTable(name = "PAI_MAPA_COM_CASCADE_DATE", joinColumns = @JoinColumn(name = "PAI_ID", nullable = false), inverseJoinColumns = @JoinColumn(name = "FILHO_ID", nullable = false))
    @Comments({ @Comment(table = "PAI_MAPA_COM_CASCADE_DATE", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_COM_CASCADE_DATE", column = "PAI_ID", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_COM_CASCADE_DATE", column = "DATA", value = "SEM COMENTARIO."),
            @Comment(table = "PAI_MAPA_COM_CASCADE_DATE", column = "FILHO_ID", value = "SEM COMENTARIO.") })
    @Alias("PMCCD")
    @Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
    @DisplayName("Mapa com cascade date")
    public Map<Date, Filho> getMapaDateComCascade() {
        return this.mapaDateComCascade;
    }

    /**
     * @param mapaDateComCascade the mapaDateComCascade to set
     */
    public void setMapaDateComCascade(Map<Date, Filho> mapaDateComCascade) {
        this.mapaDateComCascade = mapaDateComCascade;
    }
}